package cn.huanzi.qch.baseadmin.ecco.home.controller;

import cn.huanzi.qch.baseadmin.annotation.In;
import cn.huanzi.qch.baseadmin.common.controller.CommonController;
import cn.huanzi.qch.baseadmin.ecco.NodeDTO;
import cn.huanzi.qch.baseadmin.ecco.home.pojo.Comment;
import cn.huanzi.qch.baseadmin.sys.sysmenu.pojo.SysMenu;
import cn.huanzi.qch.baseadmin.sys.sysmenu.vo.SysMenuVo;
import cn.huanzi.qch.baseadmin.util.SerializeUtil;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import redis.clients.jedis.Jedis;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@RestController
@RequestMapping("/ecco/")
public class HomeController extends CommonController<SysMenuVo, SysMenu, String> {
//    @Autowired
//    private SysMenuService sysMenuService;

    @GetMapping("home")
    public ModelAndView menu(){
        System.out.println(1);
        return new ModelAndView("ecco/home2");
//        return new ModelAndView("sys/menu/menu");
    }

    @GetMapping("detail")
    public ModelAndView detail(String name,String uuid,String dir,String version){
        ModelAndView modelAndView=new ModelAndView("ecco/detail");
        String dirname="dir:"+version.substring(version.indexOf("(")+1, version.indexOf(")"))+".1";
        Jedis jedis1=new Jedis("localhost",6379);
        String basedir=jedis1.get(dirname);
        jedis1.close();

        String javadetail="";
        FileInputStream inputStream = null;
        File file=new File(basedir+"\\"+dir);
        Long filelength=file.length();
        byte[] buffer=new byte[filelength.intValue()];
        try {
            inputStream = new FileInputStream(file);
            inputStream.read(buffer);
            inputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }


        List<String> fileContent=new ArrayList<>();
        BufferedReader bufferedReader = null;
        try {
            bufferedReader = new BufferedReader( new FileReader(file));
            String tempString = null;
            while ((tempString = bufferedReader.readLine()) != null) {
                fileContent.add(tempString);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        Jedis jedis=new Jedis("localhost",6379);
        List content=jedis.lrange(uuid,0,-1);

        jedis.close();


        try {
            modelAndView.addObject("javadetail",new String(buffer,"UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        modelAndView.addObject("javaline",getContentLine(content,fileContent));
        return modelAndView;
    }


    @GetMapping("readme")
    public ModelAndView readme(){
        ModelAndView modelAndView=new ModelAndView("ecco/readme");
        File file=new File("D:\\study\\workspace1\\ecco-master\\examples\\lucene-src\\release差异分析.md");
        Long filelength=file.length();
        byte[] buffer=new byte[filelength.intValue()];
        try {
            FileInputStream inputStream = new FileInputStream(file);
            inputStream.read(buffer);
            inputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            modelAndView.addObject("content",new String(buffer,"UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return modelAndView;
    }

    public List getContentLine(List<String> content,List<String> fileContent){
        List line=new ArrayList();
        for(String a:content){
            String[] oneline=a.split("\r\n");
            for(String b:oneline){
                if(!b.trim().equals("")){
                    for(int i=0;i<fileContent.size();i++){
                        if(fileContent.get(i).contains(b.trim())){
                            line.add((i+1)+"");
                            break;
                        }
                    }
                }
            }

        }
        return line;
    }

    public String[] getJava(NodeDTO dto){
        StringBuffer stringBuffer = new StringBuffer();
        List list=new ArrayList<Integer>();
        deepArray(dto,stringBuffer,list,1);
        String[] res={stringBuffer.toString(),list.toString()};
        return res;
    }
    public void deepArray(NodeDTO dto, StringBuffer str, List<Integer> nums, int line){
        for(NodeDTO nodeDTO:dto.getChilds()){
            if(nodeDTO.getChilds().size()>0){
                str.append("{"+nodeDTO.getName()+"\n");
                if(nodeDTO.isIn()){
                    nums.add(line);
                }
                line++;
                deepArray(nodeDTO, str, nums, line);
                str.append("}\n");
                line++;
            }else {
                str.append(nodeDTO.getName()+"\n");
                if(nodeDTO.isIn()){
                    nums.add(line);
                }
                line++;
            }
        }
    }

    @GetMapping("listshow")
    public ModelAndView listshow(){
        System.out.println("访问listshow页面");
        return new ModelAndView("ecco/listshow");
//        return new ModelAndView("sys/menu/menu");
    }
    @GetMapping("compose")
    public ModelAndView compose(){
        System.out.println("访问compose页面");
        return new ModelAndView("ecco/compose");
    }
    @GetMapping("splitmodle")
    public ModelAndView splitmodle(){
        System.out.println("访问splitmodle页面");
        return new ModelAndView("ecco/splitmodle");
    }
    @GetMapping("search")
    public ModelAndView search(){
        System.out.println("访问search页面");
        return new ModelAndView("ecco/search");
    }
    @GetMapping("searchmore")
    public ModelAndView searchmore(){
        System.out.println("访问searchmore页面");
        return new ModelAndView("ecco/search_more");
    }
    @GetMapping("createproject")
    public ModelAndView createproject(){
        System.out.println("访问createproject页面");
        return new ModelAndView("ecco/createproject");
    }
    @GetMapping("workspace")
    public ModelAndView workspace(){
        System.out.println("访问workspace页面");
        return new ModelAndView("ecco/workspace");
    }

    @GetMapping("migration")
    public ModelAndView migration(){
        System.out.println("访问migration页面");
        return new ModelAndView("ecco/migration");
    }
    @GetMapping("note")
    public ModelAndView note(){
        System.out.println("访问Note页面");
        return new ModelAndView("ecco/Note");
    }
    @GetMapping("showcomment")
    public ModelAndView showcomment(String modle,String file){
        System.out.println("访问showcomment页面");
        Jedis jedis=new Jedis("localhost",6379);
        List<String> list = jedis.lrange(modle +"_"+ file, 0, -1);
        List<String> listtime = jedis.lrange(modle +"_"+ file+"_time", 0, -1);
        jedis.close();
        List<Comment> comments=new ArrayList<>();
        for(String item:list){
            comments.add(JSONObject.parseObject(item,Comment.class));
        }
        ModelAndView modelAndView=new ModelAndView("ecco/showcomment");
        modelAndView.addObject("list",comments);
        return modelAndView;
    }
    @GetMapping("addcomment")
    public ModelAndView addcomment(String name){
        ModelAndView modelAndView=new ModelAndView("ecco/addcomment");
        modelAndView.addObject("name",name);
        return modelAndView;
    }
    @GetMapping("showjavalist")
    public ModelAndView showjavalist(String release){
        System.out.println("访问showjavalist页面");
        ModelAndView modelAndView=new ModelAndView("ecco/javalist");
        Jedis jedis=new Jedis("localhost",6379);
        List<String> current=jedis.lrange("360:"+release+":current", 0, -1);
        HashMap currentmap=new HashMap();
        for(String name:current){
            String[] names=name.split("&&");
            String dir="D:\\study\\IRTool\\IRTool\\data\\lucene\\"+names[0]+"\\"+names[1]+".txt";
            StringBuffer content=new StringBuffer();
            String thisLine;
            try {
                BufferedReader in = new BufferedReader(new FileReader(dir));
                while((thisLine = in.readLine()) != null)
                    content.append(thisLine+"\r\n");// 每次读取一行，直到文件结束
                in.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            currentmap.put(name, content);
        }
        List<String> past=jedis.lrange("360:"+release+":past", 0, -1);
        HashMap pastmap=new HashMap();
        for(String name:past){
            String[] names=name.split("&&");
            String dir="D:\\study\\IRTool\\IRTool\\data\\lucene\\"+names[0]+"\\"+names[1]+".txt";
            StringBuffer content=new StringBuffer();
            String thisLine;
            try {
                BufferedReader in = new BufferedReader(new FileReader(dir));
                while((thisLine = in.readLine()) != null)
                    content.append(thisLine+"\r\n");// 每次读取一行，直到文件结束
                in.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            pastmap.put(name, content);
        }
        modelAndView.addObject("current",currentmap);
        modelAndView.addObject("past",pastmap);
        jedis.close();
        return modelAndView;
    }


    //绘制页面临时添加，可以忽略
    @GetMapping("showjavalist_expand")
    public ModelAndView showjavalist_expand(){
        ModelAndView modelAndView=new ModelAndView("ecco/javalist_expand");
        HashMap map = new HashMap();
        List<String> value=new ArrayList<>();
        value.add("/** Create a new MMapDirectory for the named location and {@link NativeFSLockFactory}.\n" +
                "   *\n" +
                "   * @param path the path of the directory\n" +
                "   * @throws IOException\n" +
                "   */\n" +
                "  public MMapDirectory(File path) throws IOException {\n" +
                "    super(path, null);\n" +
                "    setMaxChunkSize(DEFAULT_MAX_BUFF);\n" +
                "  }");
        value.add("package org.apache.lucene.store;\n" +
                "\n" +
                "/**\n" +
                " * Licensed to the Apache Software Foundation (ASF) under one or more\n" +
                " * contributor license agreements.  See the NOTICE file distributed with\n" +
                " * this work for additional information regarding copyright ownership.\n" +
                " * The ASF licenses this file to You under the Apache License, Version 2.0\n" +
                " * (the \"License\"); you may not use this file except in compliance with\n" +
                " * the License.  You may obtain a copy of the License at\n" +
                " *\n" +
                " *     http://www.apache.org/licenses/LICENSE-2.0\n" +
                " *\n" +
                " * Unless required by applicable law or agreed to in writing, software\n" +
                " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" +
                " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
                " * See the License for the specific language governing permissions and\n" +
                " * limitations under the License.\n" +
                " */\n" +
                " \n" +
                "import java.io.EOFException;\n" +
                "import java.io.IOException;\n" +
                "import java.io.File;\n" +
                "import java.io.RandomAccessFile;\n" +
                "import java.nio.ByteBuffer;\n" +
                "import java.nio.BufferUnderflowException;\n" +
                "import java.nio.channels.ClosedChannelException; // javadoc\n" +
                "import java.nio.channels.FileChannel;\n" +
                "import java.nio.channels.FileChannel.MapMode;\n" +
                "\n" +
                "import java.util.Set;\n" +
                "import java.util.WeakHashMap;\n" +
                "\n" +
                "import java.security.AccessController;\n" +
                "import java.security.PrivilegedExceptionAction;\n" +
                "import java.security.PrivilegedActionException;\n" +
                "import java.lang.reflect.Method;\n" +
                "\n" +
                "import org.apache.lucene.util.MapBackedSet;\n" +
                "import org.apache.lucene.util.Constants;\n" +
                "\n" +
                "/** File-based {@link Directory} implementation that uses\n" +
                " *  mmap for reading, and {@link\n" +
                " *  FSDirectory.FSIndexOutput} for writing.\n" +
                " *\n" +
                " * <p><b>NOTE</b>: memory mapping uses up a portion of the\n" +
                " * virtual memory address space in your process equal to the\n" +
                " * size of the file being mapped.  Before using this class,\n" +
                " * be sure your have plenty of virtual address space, e.g. by\n" +
                " * using a 64 bit JRE, or a 32 bit JRE with indexes that are\n" +
                " * guaranteed to fit within the address space.\n" +
                " * On 32 bit platforms also consult {@link #setMaxChunkSize}\n" +
                " * if you have problems with mmap failing because of fragmented\n" +
                " * address space. If you get an OutOfMemoryException, it is recommended\n" +
                " * to reduce the chunk size, until it works.\n" +
                " *\n" +
                " * <p>Due to <a href=\"http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4724038\">\n" +
                " * this bug</a> in Sun's JRE, MMapDirectory's {@link IndexInput#close}\n" +
                " * is unable to close the underlying OS file handle.  Only when GC\n" +
                " * finally collects the underlying objects, which could be quite\n" +
                " * some time later, will the file handle be closed.\n" +
                " *\n" +
                " * <p>This will consume additional transient disk usage: on Windows,\n" +
                " * attempts to delete or overwrite the files will result in an\n" +
                " * exception; on other platforms, which typically have a &quot;delete on\n" +
                " * last close&quot; semantics, while such operations will succeed, the bytes\n" +
                " * are still consuming space on disk.  For many applications this\n" +
                " * limitation is not a problem (e.g. if you have plenty of disk space,\n" +
                " * and you don't rely on overwriting files on Windows) but it's still\n" +
                " * an important limitation to be aware of.\n" +
                " *\n" +
                " * <p>This class supplies the workaround mentioned in the bug report\n" +
                " * (see {@link #setUseUnmap}), which may fail on\n" +
                " * non-Sun JVMs. It forcefully unmaps the buffer on close by using\n" +
                " * an undocumented internal cleanup functionality.\n" +
                " * {@link #UNMAP_SUPPORTED} is <code>true</code>, if the workaround\n" +
                " * can be enabled (with no guarantees).\n" +
                " * <p>\n" +
                " * <b>NOTE:</b> Accessing this class either directly or\n" +
                " * indirectly from a thread while it's interrupted can close the\n" +
                " * underlying channel immediately if at the same time the thread is\n" +
                " * blocked on IO. The channel will remain closed and subsequent access\n" +
                " * to {@link MMapDirectory} will throw a {@link ClosedChannelException}. \n" +
                " * </p>\n" +
                " */\n" +
                "public class MMapDirectory extends FSDirectory {\n" +
                "  private boolean useUnmapHack = UNMAP_SUPPORTED;\n" +
                "  public static final int DEFAULT_MAX_BUFF = Constants.JRE_IS_64BIT ? (1 << 30) : (1 << 28);\n" +
                "  private int chunkSizePower;\n" +
                "\n" +
                "  /** Create a new MMapDirectory for the named location.\n" +
                "   *\n" +
                "   * @param path the path of the directory\n" +
                "   * @param lockFactory the lock factory to use, or null for the default\n" +
                "   * ({@link NativeFSLockFactory});\n" +
                "   * @throws IOException\n" +
                "   */\n" +
                "  public MMapDirectory(File path, LockFactory lockFactory) throws IOException {\n" +
                "    super(path, lockFactory);\n" +
                "    setMaxChunkSize(DEFAULT_MAX_BUFF);\n" +
                "  }\n" +
                "\n" +
                "  /** Create a new MMapDirectory for the named location and {@link NativeFSLockFactory}.\n" +
                "   *\n" +
                "   * @param path the path of the directory\n" +
                "   * @throws IOException\n" +
                "   */\n" +
                "  public MMapDirectory(File path) throws IOException {\n" +
                "    super(path, null);\n" +
                "    setMaxChunkSize(DEFAULT_MAX_BUFF);\n" +
                "  }\n" +
                "\n" +
                "  /**\n" +
                "   * <code>true</code>, if this platform supports unmapping mmapped files.\n" +
                "   */\n" +
                "  public static final boolean UNMAP_SUPPORTED;\n" +
                "  static {\n" +
                "    boolean v;\n" +
                "    try {\n" +
                "      Class.forName(\"sun.misc.Cleaner\");\n" +
                "      Class.forName(\"java.nio.DirectByteBuffer\")\n" +
                "        .getMethod(\"cleaner\");\n" +
                "      v = true;\n" +
                "    } catch (Exception e) {\n" +
                "      v = false;\n" +
                "    }\n" +
                "    UNMAP_SUPPORTED = v;\n" +
                "  }\n" +
                "  \n" +
                "  /**\n" +
                "   * This method enables the workaround for unmapping the buffers\n" +
                "   * from address space after closing {@link IndexInput}, that is\n" +
                "   * mentioned in the bug report. This hack may fail on non-Sun JVMs.\n" +
                "   * It forcefully unmaps the buffer on close by using\n" +
                "   * an undocumented internal cleanup functionality.\n" +
                "   * <p><b>NOTE:</b> Enabling this is completely unsupported\n" +
                "   * by Java and may lead to JVM crashes if <code>IndexInput</code>\n" +
                "   * is closed while another thread is still accessing it (SIGSEGV).\n" +
                "   * @throws IllegalArgumentException if {@link #UNMAP_SUPPORTED}\n" +
                "   * is <code>false</code> and the workaround cannot be enabled.\n" +
                "   */\n" +
                "  public void setUseUnmap(final boolean useUnmapHack) {\n" +
                "    if (useUnmapHack && !UNMAP_SUPPORTED)\n" +
                "      throw new IllegalArgumentException(\"Unmap hack not supported on this platform!\");\n" +
                "    this.useUnmapHack=useUnmapHack;\n" +
                "  }\n" +
                "  \n" +
                "  /**\n" +
                "   * Returns <code>true</code>, if the unmap workaround is enabled.\n" +
                "   * @see #setUseUnmap\n" +
                "   */\n" +
                "  public boolean getUseUnmap() {\n" +
                "    return useUnmapHack;\n" +
                "  }\n" +
                "  \n" +
                "  /**\n" +
                "   * Try to unmap the buffer, this method silently fails if no support\n" +
                "   * for that in the JVM. On Windows, this leads to the fact,\n" +
                "   * that mmapped files cannot be modified or deleted.\n" +
                "   */\n" +
                "  final void cleanMapping(final ByteBuffer buffer) throws IOException {\n" +
                "    if (useUnmapHack) {\n" +
                "      try {\n" +
                "        AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {\n" +
                "          public Object run() throws Exception {\n" +
                "            final Method getCleanerMethod = buffer.getClass()\n" +
                "              .getMethod(\"cleaner\");\n" +
                "            getCleanerMethod.setAccessible(true);\n" +
                "            final Object cleaner = getCleanerMethod.invoke(buffer);\n" +
                "            if (cleaner != null) {\n" +
                "              cleaner.getClass().getMethod(\"clean\")\n" +
                "                .invoke(cleaner);\n" +
                "            }\n" +
                "            return null;\n" +
                "          }\n" +
                "        });\n" +
                "      } catch (PrivilegedActionException e) {\n" +
                "        final IOException ioe = new IOException(\"unable to unmap the mapped buffer\");\n" +
                "        ioe.initCause(e.getCause());\n" +
                "        throw ioe;\n" +
                "      }\n" +
                "    }\n" +
                "  }\n" +
                "  \n" +
                "  /**\n" +
                "   * Sets the maximum chunk size (default is 1 GiBytes for\n" +
                "   * 64 bit JVMs and 256 MiBytes for 32 bit JVMs) used for memory mapping.\n" +
                "   * Especially on 32 bit platform, the address space can be very fragmented,\n" +
                "   * so large index files cannot be mapped.\n" +
                "   * Using a lower chunk size makes the directory implementation a little\n" +
                "   * bit slower (as the correct chunk may be resolved on lots of seeks)\n" +
                "   * but the chance is higher that mmap does not fail. On 64 bit\n" +
                "   * Java platforms, this parameter should always be {@code 1 << 30},\n" +
                "   * as the address space is big enough.\n" +
                "   * <b>Please note:</b> This method always rounds down the chunk size\n" +
                "   * to a power of 2.\n" +
                "   */\n" +
                "  public final void setMaxChunkSize(final int maxChunkSize) {\n" +
                "    if (maxChunkSize <= 0)\n" +
                "      throw new IllegalArgumentException(\"Maximum chunk size for mmap must be >0\");\n" +
                "    //System.out.println(\"Requested chunk size: \"+maxChunkSize);\n" +
                "    this.chunkSizePower = 31 - Integer.numberOfLeadingZeros(maxChunkSize);\n" +
                "    assert this.chunkSizePower >= 0 && this.chunkSizePower <= 30;\n" +
                "    //System.out.println(\"Got chunk size: \"+getMaxChunkSize());\n" +
                "  }\n" +
                "  \n" +
                "  /**\n" +
                "   * Returns the current mmap chunk size.\n" +
                "   * @see #setMaxChunkSize\n" +
                "   */\n" +
                "  public final int getMaxChunkSize() {\n" +
                "    return 1 << chunkSizePower;\n" +
                "  }\n" +
                "\n" +
                "  /** Creates an IndexInput for the file with the given name. */\n" +
                "  @Override\n" +
                "  public IndexInput openInput(String name, int bufferSize) throws IOException {\n" +
                "    ensureOpen();\n" +
                "    File f = new File(getDirectory(), name);\n" +
                "    RandomAccessFile raf = new RandomAccessFile(f, \"r\");\n" +
                "    try {\n" +
                "      return new MMapIndexInput(\"MMapIndexInput(path=\\\"\" + f + \"\\\")\", raf, chunkSizePower);\n" +
                "    } finally {\n" +
                "      raf.close();\n" +
                "    }\n" +
                "  }\n" +
                "\n" +
                "  // Because Java's ByteBuffer uses an int to address the\n" +
                "  // values, it's necessary to access a file >\n" +
                "  // Integer.MAX_VALUE in size using multiple byte buffers.\n" +
                "  private final class MMapIndexInput extends IndexInput {\n" +
                "  \n" +
                "    private ByteBuffer[] buffers;\n" +
                "  \n" +
                "    private final long length, chunkSizeMask, chunkSize;\n" +
                "    private final int chunkSizePower;\n" +
                "  \n" +
                "    private int curBufIndex;\n" +
                "  \n" +
                "    private ByteBuffer curBuf; // redundant for speed: buffers[curBufIndex]\n" +
                "  \n" +
                "    private boolean isClone = false;\n" +
                "    private final Set<MMapIndexInput> clones = new MapBackedSet<MMapIndexInput>(new WeakHashMap<MMapIndexInput,Boolean>());\n" +
                "\n" +
                "    MMapIndexInput(String resourceDescription, RandomAccessFile raf, int chunkSizePower) throws IOException {\n" +
                "      super(resourceDescription);\n" +
                "      this.length = raf.length();\n" +
                "      this.chunkSizePower = chunkSizePower;\n" +
                "      this.chunkSize = 1L << chunkSizePower;\n" +
                "      this.chunkSizeMask = chunkSize - 1L;\n" +
                "      \n" +
                "      if (chunkSizePower < 0 || chunkSizePower > 30)\n" +
                "        throw new IllegalArgumentException(\"Invalid chunkSizePower used for ByteBuffer size: \" + chunkSizePower);\n" +
                "      \n" +
                "      if ((length >>> chunkSizePower) >= Integer.MAX_VALUE)\n" +
                "        throw new IllegalArgumentException(\"RandomAccessFile too big for chunk size: \" + raf.toString());\n" +
                "      \n" +
                "      // we always allocate one more buffer, the last one may be a 0 byte one\n" +
                "      final int nrBuffers = (int) (length >>> chunkSizePower) + 1;\n" +
                "      \n" +
                "      //System.out.println(\"length=\"+length+\", chunkSizePower=\" + chunkSizePower + \", chunkSizeMask=\" + chunkSizeMask + \", nrBuffers=\" + nrBuffers);\n" +
                "      \n" +
                "      this.buffers = new ByteBuffer[nrBuffers];\n" +
                "      \n" +
                "      long bufferStart = 0L;\n" +
                "      FileChannel rafc = raf.getChannel();\n" +
                "      for (int bufNr = 0; bufNr < nrBuffers; bufNr++) { \n" +
                "        int bufSize = (int) ( (length > (bufferStart + chunkSize))\n" +
                "          ? chunkSize\n" +
                "          : (length - bufferStart)\n" +
                "        );\n" +
                "        this.buffers[bufNr] = rafc.map(MapMode.READ_ONLY, bufferStart, bufSize);\n" +
                "        bufferStart += bufSize;\n" +
                "      }\n" +
                "      seek(0L);\n" +
                "    }\n" +
                "  \n" +
                "    @Override\n" +
                "    public byte readByte() throws IOException {\n" +
                "      try {\n" +
                "        return curBuf.get();\n" +
                "      } catch (BufferUnderflowException e) {\n" +
                "        do {\n" +
                "          curBufIndex++;\n" +
                "          if (curBufIndex >= buffers.length) {\n" +
                "            throw new EOFException(\"read past EOF: \" + this);\n" +
                "          }\n" +
                "          curBuf = buffers[curBufIndex];\n" +
                "          curBuf.position(0);\n" +
                "        } while (!curBuf.hasRemaining());\n" +
                "        return curBuf.get();\n" +
                "      } catch (NullPointerException npe) {\n" +
                "        throw new AlreadyClosedException(\"MMapIndexInput already closed: \" + this);\n" +
                "      }\n" +
                "    }\n" +
                "  \n" +
                "    @Override\n" +
                "    public void readBytes(byte[] b, int offset, int len) throws IOException {\n" +
                "      try {\n" +
                "        curBuf.get(b, offset, len);\n" +
                "      } catch (BufferUnderflowException e) {\n" +
                "        int curAvail = curBuf.remaining();\n" +
                "        while (len > curAvail) {\n" +
                "          curBuf.get(b, offset, curAvail);\n" +
                "          len -= curAvail;\n" +
                "          offset += curAvail;\n" +
                "          curBufIndex++;\n" +
                "          if (curBufIndex >= buffers.length) {\n" +
                "            throw new EOFException(\"read past EOF: \" + this);\n" +
                "          }\n" +
                "          curBuf = buffers[curBufIndex];\n" +
                "          curBuf.position(0);\n" +
                "          curAvail = curBuf.remaining();\n" +
                "        }\n" +
                "        curBuf.get(b, offset, len);\n" +
                "      } catch (NullPointerException npe) {\n" +
                "        throw new AlreadyClosedException(\"MMapIndexInput already closed: \" + this);\n" +
                "      }\n" +
                "    }\n" +
                "  \n" +
                "    @Override\n" +
                "    public short readShort() throws IOException {\n" +
                "      try {\n" +
                "        return curBuf.getShort();\n" +
                "      } catch (BufferUnderflowException e) {\n" +
                "        return super.readShort();\n" +
                "      } catch (NullPointerException npe) {\n" +
                "        throw new AlreadyClosedException(\"MMapIndexInput already closed: \" + this);\n" +
                "      }\n" +
                "    }\n" +
                "\n" +
                "    @Override\n" +
                "    public int readInt() throws IOException {\n" +
                "      try {\n" +
                "        return curBuf.getInt();\n" +
                "      } catch (BufferUnderflowException e) {\n" +
                "        return super.readInt();\n" +
                "      } catch (NullPointerException npe) {\n" +
                "        throw new AlreadyClosedException(\"MMapIndexInput already closed: \" + this);\n" +
                "      }\n" +
                "    }\n" +
                "\n" +
                "    @Override\n" +
                "    public long readLong() throws IOException {\n" +
                "      try {\n" +
                "        return curBuf.getLong();\n" +
                "      } catch (BufferUnderflowException e) {\n" +
                "        return super.readLong();\n" +
                "      } catch (NullPointerException npe) {\n" +
                "        throw new AlreadyClosedException(\"MMapIndexInput already closed: \" + this);\n" +
                "      }\n" +
                "    }\n" +
                "    \n" +
                "    @Override\n" +
                "    public long getFilePointer() {\n" +
                "      try {\n" +
                "        return (((long) curBufIndex) << chunkSizePower) + curBuf.position();\n" +
                "      } catch (NullPointerException npe) {\n" +
                "        throw new AlreadyClosedException(\"MMapIndexInput already closed: \" + this);\n" +
                "      }\n" +
                "    }\n" +
                "  \n" +
                "    @Override\n" +
                "    public void seek(long pos) throws IOException {\n" +
                "      // we use >> here to preserve negative, so we will catch AIOOBE:\n" +
                "      final int bi = (int) (pos >> chunkSizePower);\n" +
                "      try {\n" +
                "        final ByteBuffer b = buffers[bi];\n" +
                "        b.position((int) (pos & chunkSizeMask));\n" +
                "        // write values, on exception all is unchanged\n" +
                "        this.curBufIndex = bi;\n" +
                "        this.curBuf = b;\n" +
                "      } catch (ArrayIndexOutOfBoundsException aioobe) {\n" +
                "        if (pos < 0L) {\n" +
                "          throw new IllegalArgumentException(\"Seeking to negative position: \" + this);\n" +
                "        }\n" +
                "        throw new EOFException(\"seek past EOF: \" + this);\n" +
                "      } catch (IllegalArgumentException iae) {\n" +
                "        if (pos < 0L) {\n" +
                "          throw new IllegalArgumentException(\"Seeking to negative position: \" + this);\n" +
                "        }\n" +
                "        throw new EOFException(\"seek past EOF: \" + this);\n" +
                "      } catch (NullPointerException npe) {\n" +
                "        throw new AlreadyClosedException(\"MMapIndexInput already closed: \" + this);\n" +
                "      }\n" +
                "    }\n" +
                "  \n" +
                "    @Override\n" +
                "    public long length() {\n" +
                "      return length;\n" +
                "    }\n" +
                "  \n" +
                "    @Override\n" +
                "    public Object clone() {\n" +
                "      if (buffers == null) {\n" +
                "        throw new AlreadyClosedException(\"MMapIndexInput already closed: \" + this);\n" +
                "      }\n" +
                "      final MMapIndexInput clone = (MMapIndexInput)super.clone();\n" +
                "      clone.isClone = true;\n" +
                "      // we keep clone.clones, so it shares the same map with original and we have no additional cost on clones\n" +
                "      assert clone.clones == this.clones;\n" +
                "      clone.buffers = new ByteBuffer[buffers.length];\n" +
                "      for (int bufNr = 0; bufNr < buffers.length; bufNr++) {\n" +
                "        clone.buffers[bufNr] = buffers[bufNr].duplicate();\n" +
                "      }\n" +
                "      try {\n" +
                "        clone.seek(getFilePointer());\n" +
                "      } catch(IOException ioe) {\n" +
                "        throw new RuntimeException(\"Should never happen: \" + this, ioe);\n" +
                "      }\n" +
                "      \n" +
                "      // register the new clone in our clone list to clean it up on closing:\n" +
                "      synchronized(this.clones) {\n" +
                "        this.clones.add(clone);\n" +
                "      }\n" +
                "      \n" +
                "      return clone;\n" +
                "    }\n" +
                "    \n" +
                "    private void unsetBuffers() {\n" +
                "      buffers = null;\n" +
                "      curBuf = null;\n" +
                "      curBufIndex = 0;\n" +
                "    }\n" +
                "  \n" +
                "    @Override\n" +
                "    public void close() throws IOException {\n" +
                "      try {\n" +
                "        if (isClone || buffers == null) return;\n" +
                "        \n" +
                "        // for extra safety unset also all clones' buffers:\n" +
                "        synchronized(this.clones) {\n" +
                "          for (final MMapIndexInput clone : this.clones) {\n" +
                "            assert clone.isClone;\n" +
                "            clone.unsetBuffers();\n" +
                "          }\n" +
                "          this.clones.clear();\n" +
                "        }\n" +
                "        \n" +
                "        curBuf = null; curBufIndex = 0; // nuke curr pointer early\n" +
                "        for (int bufNr = 0; bufNr < buffers.length; bufNr++) {\n" +
                "          cleanMapping(buffers[bufNr]);\n" +
                "        }\n" +
                "      } finally {\n" +
                "        unsetBuffers();\n" +
                "      }\n" +
                "    }\n" +
                "\n" +
                "    // make sure we have identity on equals/hashCode for WeakHashMap\n" +
                "    @Override\n" +
                "    public int hashCode() {\n" +
                "      return System.identityHashCode(this);\n" +
                "    }\n" +
                "\n" +
                "    // make sure we have identity on equals/hashCode for WeakHashMap\n" +
                "    @Override\n" +
                "    public boolean equals(Object obj) {\n" +
                "      return obj == this;\n" +
                "    }\n" +
                "  }\n" +
                "}\n");

        map.put("MMapDirectory.java", value);

        List value1=new ArrayList();
        value1.add(" public static final int DEFAULT_READ_CHUNK_SIZE = Constants.JRE_IS_64BIT ? Integer.MAX_VALUE : 100 * 1024 * 1024;\n" +
                "\n" +
                "  protected final File directory; // The underlying filesystem directory\n" +
                "  protected final Set<String> staleFiles = synchronizedSet(new HashSet<String>()); // Files written, but not yet sync'ed\n" +
                "  private int chunkSize = DEFAULT_READ_CHUNK_SIZE; // LUCENE-1566\n" +
                "\n" +
                "  // returns the canonical version of the directory, creating it if it doesn't exist.\n" +
                "  private static File getCanonicalPath(File file) throws IOException {\n" +
                "    return new File(file.getCanonicalPath());\n" +
                "  }");
        value1.add("package org.apache.lucene.store;\n" +
                "\n" +
                "/**\n" +
                " * Licensed to the Apache Software Foundation (ASF) under one or more\n" +
                " * contributor license agreements.  See the NOTICE file distributed with\n" +
                " * this work for additional information regarding copyright ownership.\n" +
                " * The ASF licenses this file to You under the Apache License, Version 2.0\n" +
                " * (the \"License\"); you may not use this file except in compliance with\n" +
                " * the License.  You may obtain a copy of the License at\n" +
                " *\n" +
                " *     http://www.apache.org/licenses/LICENSE-2.0\n" +
                " *\n" +
                " * Unless required by applicable law or agreed to in writing, software\n" +
                " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" +
                " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
                " * See the License for the specific language governing permissions and\n" +
                " * limitations under the License.\n" +
                " */\n" +
                "\n" +
                "import java.io.File;\n" +
                "import java.io.FileNotFoundException;\n" +
                "import java.io.FilenameFilter;\n" +
                "import java.io.IOException;\n" +
                "import java.io.RandomAccessFile;\n" +
                "\n" +
                "import java.util.Collection;\n" +
                "import static java.util.Collections.synchronizedSet;\n" +
                "\n" +
                "import java.util.Collections;\n" +
                "import java.util.HashSet;\n" +
                "import java.util.Set;\n" +
                "import java.util.concurrent.Future;\n" +
                "\n" +
                "import org.apache.lucene.util.ThreadInterruptedException;\n" +
                "import org.apache.lucene.util.Constants;\n" +
                "\n" +
                "/**\n" +
                " * <a name=\"subclasses\"/>\n" +
                " * Base class for Directory implementations that store index\n" +
                " * files in the file system.  There are currently three core\n" +
                " * subclasses:\n" +
                " *\n" +
                " * <ul>\n" +
                " *\n" +
                " *  <li> {@link SimpleFSDirectory} is a straightforward\n" +
                " *       implementation using java.io.RandomAccessFile.\n" +
                " *       However, it has poor concurrent performance\n" +
                " *       (multiple threads will bottleneck) as it\n" +
                " *       synchronizes when multiple threads read from the\n" +
                " *       same file.\n" +
                " *\n" +
                " *  <li> {@link NIOFSDirectory} uses java.nio's\n" +
                " *       FileChannel's positional io when reading to avoid\n" +
                " *       synchronization when reading from the same file.\n" +
                " *       Unfortunately, due to a Windows-only <a\n" +
                " *       href=\"http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6265734\">Sun\n" +
                " *       JRE bug</a> this is a poor choice for Windows, but\n" +
                " *       on all other platforms this is the preferred\n" +
                " *       choice. Applications using {@link Thread#interrupt()} or\n" +
                " *       {@link Future#cancel(boolean)} should use\n" +
                " *       {@link SimpleFSDirectory} instead. See {@link NIOFSDirectory} java doc\n" +
                " *       for details.\n" +
                " *        \n" +
                " *        \n" +
                " *\n" +
                " *  <li> {@link MMapDirectory} uses memory-mapped IO when\n" +
                " *       reading. This is a good choice if you have plenty\n" +
                " *       of virtual memory relative to your index size, eg\n" +
                " *       if you are running on a 64 bit JRE, or you are\n" +
                " *       running on a 32 bit JRE but your index sizes are\n" +
                " *       small enough to fit into the virtual memory space.\n" +
                " *       Java has currently the limitation of not being able to\n" +
                " *       unmap files from user code. The files are unmapped, when GC\n" +
                " *       releases the byte buffers. Due to\n" +
                " *       <a href=\"http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4724038\">\n" +
                " *       this bug</a> in Sun's JRE, MMapDirectory's {@link IndexInput#close}\n" +
                " *       is unable to close the underlying OS file handle. Only when\n" +
                " *       GC finally collects the underlying objects, which could be\n" +
                " *       quite some time later, will the file handle be closed.\n" +
                " *       This will consume additional transient disk usage: on Windows,\n" +
                " *       attempts to delete or overwrite the files will result in an\n" +
                " *       exception; on other platforms, which typically have a &quot;delete on\n" +
                " *       last close&quot; semantics, while such operations will succeed, the bytes\n" +
                " *       are still consuming space on disk.  For many applications this\n" +
                " *       limitation is not a problem (e.g. if you have plenty of disk space,\n" +
                " *       and you don't rely on overwriting files on Windows) but it's still\n" +
                " *       an important limitation to be aware of. This class supplies a\n" +
                " *       (possibly dangerous) workaround mentioned in the bug report,\n" +
                " *       which may fail on non-Sun JVMs.\n" +
                " *       \n" +
                " *       Applications using {@link Thread#interrupt()} or\n" +
                " *       {@link Future#cancel(boolean)} should use\n" +
                " *       {@link SimpleFSDirectory} instead. See {@link MMapDirectory}\n" +
                " *       java doc for details.\n" +
                " * </ul>\n" +
                " *\n" +
                " * Unfortunately, because of system peculiarities, there is\n" +
                " * no single overall best implementation.  Therefore, we've\n" +
                " * added the {@link #open} method, to allow Lucene to choose\n" +
                " * the best FSDirectory implementation given your\n" +
                " * environment, and the known limitations of each\n" +
                " * implementation.  For users who have no reason to prefer a\n" +
                " * specific implementation, it's best to simply use {@link\n" +
                " * #open}.  For all others, you should instantiate the\n" +
                " * desired implementation directly.\n" +
                " *\n" +
                " * <p>The locking implementation is by default {@link\n" +
                " * NativeFSLockFactory}, but can be changed by\n" +
                " * passing in a custom {@link LockFactory} instance.\n" +
                " *\n" +
                " * @see Directory\n" +
                " */\n" +
                "public abstract class FSDirectory extends Directory {\n" +
                "\n" +
                "  /**\n" +
                "   * Default read chunk size.  This is a conditional default: on 32bit JVMs, it defaults to 100 MB.  On 64bit JVMs, it's\n" +
                "   * <code>Integer.MAX_VALUE</code>.\n" +
                "   *\n" +
                "   * @see #setReadChunkSize\n" +
                "   */\n" +
                "  public static final int DEFAULT_READ_CHUNK_SIZE = Constants.JRE_IS_64BIT ? Integer.MAX_VALUE : 100 * 1024 * 1024;\n" +
                "\n" +
                "  protected final File directory; // The underlying filesystem directory\n" +
                "  protected final Set<String> staleFiles = synchronizedSet(new HashSet<String>()); // Files written, but not yet sync'ed\n" +
                "  private int chunkSize = DEFAULT_READ_CHUNK_SIZE; // LUCENE-1566\n" +
                "\n" +
                "  // returns the canonical version of the directory, creating it if it doesn't exist.\n" +
                "  private static File getCanonicalPath(File file) throws IOException {\n" +
                "    return new File(file.getCanonicalPath());\n" +
                "  }\n" +
                "\n" +
                "  /** Create a new FSDirectory for the named location (ctor for subclasses).\n" +
                "   * @param path the path of the directory\n" +
                "   * @param lockFactory the lock factory to use, or null for the default\n" +
                "   * ({@link NativeFSLockFactory});\n" +
                "   * @throws IOException\n" +
                "   */\n" +
                "  protected FSDirectory(File path, LockFactory lockFactory) throws IOException {\n" +
                "    // new ctors use always NativeFSLockFactory as default:\n" +
                "    if (lockFactory == null) {\n" +
                "      lockFactory = new NativeFSLockFactory();\n" +
                "    }\n" +
                "    directory = getCanonicalPath(path);\n" +
                "\n" +
                "    if (directory.exists() && !directory.isDirectory())\n" +
                "      throw new NoSuchDirectoryException(\"file '\" + directory + \"' exists but is not a directory\");\n" +
                "\n" +
                "    setLockFactory(lockFactory);\n" +
                "  }\n" +
                "\n" +
                "  /** Creates an FSDirectory instance, trying to pick the\n" +
                "   *  best implementation given the current environment.\n" +
                "   *  The directory returned uses the {@link NativeFSLockFactory}.\n" +
                "   *\n" +
                "   *  <p>Currently this returns {@link MMapDirectory} for most Solaris\n" +
                "   *  and Windows 64-bit JREs, {@link NIOFSDirectory} for other\n" +
                "   *  non-Windows JREs, and {@link SimpleFSDirectory} for other\n" +
                "   *  JREs on Windows. It is highly recommended that you consult the\n" +
                "   *  implementation's documentation for your platform before\n" +
                "   *  using this method.\n" +
                "   *\n" +
                "   * <p><b>NOTE</b>: this method may suddenly change which\n" +
                "   * implementation is returned from release to release, in\n" +
                "   * the event that higher performance defaults become\n" +
                "   * possible; if the precise implementation is important to\n" +
                "   * your application, please instantiate it directly,\n" +
                "   * instead. For optimal performance you should consider using\n" +
                "   * {@link MMapDirectory} on 64 bit JVMs.\n" +
                "   *\n" +
                "   * <p>See <a href=\"#subclasses\">above</a> */\n" +
                "  public static FSDirectory open(File path) throws IOException {\n" +
                "    return open(path, null);\n" +
                "  }\n" +
                "\n" +
                "  /** Just like {@link #open(File)}, but allows you to\n" +
                "   *  also specify a custom {@link LockFactory}. */\n" +
                "  public static FSDirectory open(File path, LockFactory lockFactory) throws IOException {\n" +
                "    if ((Constants.WINDOWS || Constants.SUN_OS || Constants.LINUX)\n" +
                "          && Constants.JRE_IS_64BIT && MMapDirectory.UNMAP_SUPPORTED) {\n" +
                "      return new MMapDirectory(path, lockFactory);\n" +
                "    } else if (Constants.WINDOWS) {\n" +
                "      return new SimpleFSDirectory(path, lockFactory);\n" +
                "    } else {\n" +
                "      return new NIOFSDirectory(path, lockFactory);\n" +
                "    }\n" +
                "  }\n" +
                "\n" +
                "  @Override\n" +
                "  public void setLockFactory(LockFactory lockFactory) throws IOException {\n" +
                "    super.setLockFactory(lockFactory);\n" +
                "\n" +
                "    // for filesystem based LockFactory, delete the lockPrefix, if the locks are placed\n" +
                "    // in index dir. If no index dir is given, set ourselves\n" +
                "    if (lockFactory instanceof FSLockFactory) {\n" +
                "      final FSLockFactory lf = (FSLockFactory) lockFactory;\n" +
                "      final File dir = lf.getLockDir();\n" +
                "      // if the lock factory has no lockDir set, use the this directory as lockDir\n" +
                "      if (dir == null) {\n" +
                "        lf.setLockDir(directory);\n" +
                "        lf.setLockPrefix(null);\n" +
                "      } else if (dir.getCanonicalPath().equals(directory.getCanonicalPath())) {\n" +
                "        lf.setLockPrefix(null);\n" +
                "      }\n" +
                "    }\n" +
                "\n" +
                "  }\n" +
                "  \n" +
                "  /** Lists all files (not subdirectories) in the\n" +
                "   *  directory.  This method never returns null (throws\n" +
                "   *  {@link IOException} instead).\n" +
                "   *\n" +
                "   *  @throws NoSuchDirectoryException if the directory\n" +
                "   *   does not exist, or does exist but is not a\n" +
                "   *   directory.\n" +
                "   *  @throws IOException if list() returns null */\n" +
                "  public static String[] listAll(File dir) throws IOException {\n" +
                "    if (!dir.exists())\n" +
                "      throw new NoSuchDirectoryException(\"directory '\" + dir + \"' does not exist\");\n" +
                "    else if (!dir.isDirectory())\n" +
                "      throw new NoSuchDirectoryException(\"file '\" + dir + \"' exists but is not a directory\");\n" +
                "\n" +
                "    // Exclude subdirs\n" +
                "    String[] result = dir.list(new FilenameFilter() {\n" +
                "        public boolean accept(File dir, String file) {\n" +
                "          return !new File(dir, file).isDirectory();\n" +
                "        }\n" +
                "      });\n" +
                "\n" +
                "    if (result == null)\n" +
                "      throw new IOException(\"directory '\" + dir + \"' exists and is a directory, but cannot be listed: list() returned null\");\n" +
                "\n" +
                "    return result;\n" +
                "  }\n" +
                "\n" +
                "  /** Lists all files (not subdirectories) in the\n" +
                "   * directory.\n" +
                "   * @see #listAll(File) */\n" +
                "  @Override\n" +
                "  public String[] listAll() throws IOException {\n" +
                "    ensureOpen();\n" +
                "    return listAll(directory);\n" +
                "  }\n" +
                "\n" +
                "  /** Returns true iff a file with the given name exists. */\n" +
                "  @Override\n" +
                "  public boolean fileExists(String name) {\n" +
                "    ensureOpen();\n" +
                "    File file = new File(directory, name);\n" +
                "    return file.exists();\n" +
                "  }\n" +
                "\n" +
                "  /** Returns the time the named file was last modified. */\n" +
                "  @Override\n" +
                "  public long fileModified(String name) {\n" +
                "    ensureOpen();\n" +
                "    File file = new File(directory, name);\n" +
                "    return file.lastModified();\n" +
                "  }\n" +
                "\n" +
                "  /** Returns the time the named file was last modified. */\n" +
                "  public static long fileModified(File directory, String name) {\n" +
                "    File file = new File(directory, name);\n" +
                "    return file.lastModified();\n" +
                "  }\n" +
                "\n" +
                "  /** Set the modified time of an existing file to now.\n" +
                "   *  @deprecated Lucene never uses this API; it will be\n" +
                "   *  removed in 4.0. */\n" +
                "  @Override\n" +
                "  @Deprecated\n" +
                "  public void touchFile(String name) {\n" +
                "    ensureOpen();\n" +
                "    File file = new File(directory, name);\n" +
                "    file.setLastModified(System.currentTimeMillis());\n" +
                "  }\n" +
                "\n" +
                "  /** Returns the length in bytes of a file in the directory. */\n" +
                "  @Override\n" +
                "  public long fileLength(String name) throws IOException {\n" +
                "    ensureOpen();\n" +
                "    File file = new File(directory, name);\n" +
                "    final long len = file.length();\n" +
                "    if (len == 0 && !file.exists()) {\n" +
                "      throw new FileNotFoundException(name);\n" +
                "    } else {\n" +
                "      return len;\n" +
                "    }\n" +
                "  }\n" +
                "\n" +
                "  /** Removes an existing file in the directory. */\n" +
                "  @Override\n" +
                "  public void deleteFile(String name) throws IOException {\n" +
                "    ensureOpen();\n" +
                "    File file = new File(directory, name);\n" +
                "    if (!file.delete())\n" +
                "      throw new IOException(\"Cannot delete \" + file);\n" +
                "    staleFiles.remove(name);\n" +
                "  }\n" +
                "\n" +
                "  /** Creates an IndexOutput for the file with the given name. */\n" +
                "  @Override\n" +
                "  public IndexOutput createOutput(String name) throws IOException {\n" +
                "    ensureOpen();\n" +
                "\n" +
                "    ensureCanWrite(name);\n" +
                "    return new FSIndexOutput(this, name);\n" +
                "  }\n" +
                "\n" +
                "  protected void ensureCanWrite(String name) throws IOException {\n" +
                "    if (!directory.exists())\n" +
                "      if (!directory.mkdirs())\n" +
                "        throw new IOException(\"Cannot create directory: \" + directory);\n" +
                "\n" +
                "    File file = new File(directory, name);\n" +
                "    if (file.exists() && !file.delete())          // delete existing, if any\n" +
                "      throw new IOException(\"Cannot overwrite: \" + file);\n" +
                "  }\n" +
                "\n" +
                "  protected void onIndexOutputClosed(FSIndexOutput io) {\n" +
                "    staleFiles.add(io.name);\n" +
                "  }\n" +
                "\n" +
                "  @Deprecated\n" +
                "  @Override\n" +
                "  public void sync(String name) throws IOException {\n" +
                "    sync(Collections.singleton(name));\n" +
                "  }\n" +
                "\n" +
                "  @Override\n" +
                "  public void sync(Collection<String> names) throws IOException {\n" +
                "    ensureOpen();\n" +
                "    Set<String> toSync = new HashSet<String>(names);\n" +
                "    toSync.retainAll(staleFiles);\n" +
                "\n" +
                "    for (String name : toSync)\n" +
                "      fsync(name);\n" +
                "\n" +
                "    staleFiles.removeAll(toSync);\n" +
                "  }\n" +
                "\n" +
                "  // Inherit javadoc\n" +
                "  @Override\n" +
                "  public IndexInput openInput(String name) throws IOException {\n" +
                "    ensureOpen();\n" +
                "    return openInput(name, BufferedIndexInput.BUFFER_SIZE);\n" +
                "  }\n" +
                "\n" +
                "  @Override\n" +
                "  public String getLockID() {\n" +
                "    ensureOpen();\n" +
                "    String dirName;                               // name to be hashed\n" +
                "    try {\n" +
                "      dirName = directory.getCanonicalPath();\n" +
                "    } catch (IOException e) {\n" +
                "      throw new RuntimeException(e.toString(), e);\n" +
                "    }\n" +
                "\n" +
                "    int digest = 0;\n" +
                "    for(int charIDX=0;charIDX<dirName.length();charIDX++) {\n" +
                "      final char ch = dirName.charAt(charIDX);\n" +
                "      digest = 31 * digest + ch;\n" +
                "    }\n" +
                "    return \"lucene-\" + Integer.toHexString(digest);\n" +
                "  }\n" +
                "\n" +
                "  /** Closes the store to future operations. */\n" +
                "  @Override\n" +
                "  public synchronized void close() {\n" +
                "    isOpen = false;\n" +
                "  }\n" +
                "\n" +
                "  /** @deprecated Use {@link #getDirectory} instead. */\n" +
                "  @Deprecated\n" +
                "  public File getFile() {\n" +
                "    return getDirectory();\n" +
                "  }\n" +
                "\n" +
                "  /** @return the underlying filesystem directory */\n" +
                "  public File getDirectory() {\n" +
                "    ensureOpen();\n" +
                "    return directory;\n" +
                "  }\n" +
                "\n" +
                "  /** For debug output. */\n" +
                "  @Override\n" +
                "  public String toString() {\n" +
                "    return this.getClass().getName() + \"@\" + directory + \" lockFactory=\" + getLockFactory();\n" +
                "  }\n" +
                "\n" +
                "  /**\n" +
                "   * Sets the maximum number of bytes read at once from the\n" +
                "   * underlying file during {@link IndexInput#readBytes}.\n" +
                "   * The default value is {@link #DEFAULT_READ_CHUNK_SIZE};\n" +
                "   *\n" +
                "   * <p> This was introduced due to <a\n" +
                "   * href=\"http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6478546\">Sun\n" +
                "   * JVM Bug 6478546</a>, which throws an incorrect\n" +
                "   * OutOfMemoryError when attempting to read too many bytes\n" +
                "   * at once.  It only happens on 32bit JVMs with a large\n" +
                "   * maximum heap size.</p>\n" +
                "   *\n" +
                "   * <p>Changes to this value will not impact any\n" +
                "   * already-opened {@link IndexInput}s.  You should call\n" +
                "   * this before attempting to open an index on the\n" +
                "   * directory.</p>\n" +
                "   *\n" +
                "   * <p> <b>NOTE</b>: This value should be as large as\n" +
                "   * possible to reduce any possible performance impact.  If\n" +
                "   * you still encounter an incorrect OutOfMemoryError,\n" +
                "   * trying lowering the chunk size.</p>\n" +
                "   */\n" +
                "  public final void setReadChunkSize(int chunkSize) {\n" +
                "    // LUCENE-1566\n" +
                "    if (chunkSize <= 0) {\n" +
                "      throw new IllegalArgumentException(\"chunkSize must be positive\");\n" +
                "    }\n" +
                "    if (!Constants.JRE_IS_64BIT) {\n" +
                "      this.chunkSize = chunkSize;\n" +
                "    }\n" +
                "  }\n" +
                "\n" +
                "  /**\n" +
                "   * The maximum number of bytes to read at once from the\n" +
                "   * underlying file during {@link IndexInput#readBytes}.\n" +
                "   * @see #setReadChunkSize\n" +
                "   */\n" +
                "  public final int getReadChunkSize() {\n" +
                "    // LUCENE-1566\n" +
                "    return chunkSize;\n" +
                "  }\n" +
                "\n" +
                "  protected static class FSIndexOutput extends BufferedIndexOutput {\n" +
                "    private final FSDirectory parent;\n" +
                "    private final String name;\n" +
                "    private final RandomAccessFile file;\n" +
                "    private volatile boolean isOpen; // remember if the file is open, so that we don't try to close it more than once\n" +
                "\n" +
                "    public FSIndexOutput(FSDirectory parent, String name) throws IOException {\n" +
                "      this.parent = parent;\n" +
                "      this.name = name;\n" +
                "      file = new RandomAccessFile(new File(parent.directory, name), \"rw\");\n" +
                "      isOpen = true;\n" +
                "    }\n" +
                "\n" +
                "    /** output methods: */\n" +
                "    @Override\n" +
                "    public void flushBuffer(byte[] b, int offset, int size) throws IOException {\n" +
                "      file.write(b, offset, size);\n" +
                "    }\n" +
                "    \n" +
                "    @Override\n" +
                "    public void close() throws IOException {\n" +
                "      parent.onIndexOutputClosed(this);\n" +
                "      // only close the file if it has not been closed yet\n" +
                "      if (isOpen) {\n" +
                "        boolean success = false;\n" +
                "        try {\n" +
                "          super.close();\n" +
                "          success = true;\n" +
                "        } finally {\n" +
                "          isOpen = false;\n" +
                "          if (!success) {\n" +
                "            try {\n" +
                "              file.close();\n" +
                "            } catch (Throwable t) {\n" +
                "              // Suppress so we don't mask original exception\n" +
                "            }\n" +
                "          } else {\n" +
                "            file.close();\n" +
                "          }\n" +
                "        }\n" +
                "      }\n" +
                "    }\n" +
                "\n" +
                "    /** Random-access methods */\n" +
                "    @Override\n" +
                "    public void seek(long pos) throws IOException {\n" +
                "      super.seek(pos);\n" +
                "      file.seek(pos);\n" +
                "    }\n" +
                "\n" +
                "    @Override\n" +
                "    public long length() throws IOException {\n" +
                "      return file.length();\n" +
                "    }\n" +
                "\n" +
                "    @Override\n" +
                "    public void setLength(long length) throws IOException {\n" +
                "      file.setLength(length);\n" +
                "    }\n" +
                "  }\n" +
                "\n" +
                "  protected void fsync(String name) throws IOException {\n" +
                "    File fullFile = new File(directory, name);\n" +
                "    boolean success = false;\n" +
                "    int retryCount = 0;\n" +
                "    IOException exc = null;\n" +
                "    while (!success && retryCount < 5) {\n" +
                "      retryCount++;\n" +
                "      RandomAccessFile file = null;\n" +
                "      try {\n" +
                "        try {\n" +
                "          file = new RandomAccessFile(fullFile, \"rw\");\n" +
                "          file.getFD().sync();\n" +
                "          success = true;\n" +
                "        } finally {\n" +
                "          if (file != null)\n" +
                "            file.close();\n" +
                "        }\n" +
                "      } catch (IOException ioe) {\n" +
                "        if (exc == null)\n" +
                "          exc = ioe;\n" +
                "        try {\n" +
                "          // Pause 5 msec\n" +
                "          Thread.sleep(5);\n" +
                "        } catch (InterruptedException ie) {\n" +
                "          throw new ThreadInterruptedException(ie);\n" +
                "        }\n" +
                "      }\n" +
                "    }\n" +
                "    if (!success)\n" +
                "      // Throw original exception\n" +
                "      throw exc;\n" +
                "  }\n" +
                "}\n");
        List value2=new ArrayList();
        value2.add(" @Deprecated\n" +
                "  public static void copy(Directory src, Directory dest, boolean closeDirSrc) throws IOException {\n" +
                "    IndexFileNameFilter filter = IndexFileNameFilter.getFilter();\n" +
                "    for (String file : src.listAll()) {\n" +
                "      if (filter.accept(null, file)) {\n" +
                "        src.copy(dest, file, file);\n" +
                "      }\n" +
                "    }\n" +
                "    if (closeDirSrc) {\n" +
                "      src.close();\n" +
                "    }\n" +
                "  }");
        value2.add("");

        map.put("FSDirectory", value1);
        map.put("Directory", value2);
        modelAndView.addObject("current",map);
        return modelAndView;
    }

}
